package com.demo.singleton03;

/**
 *  懒汉式单例类.在第一次调用的时候实例化自己
 *
 *  Singleton 通过私有化构造函数，避免类在外部被实例化，而且只能通过 getInstance()
 *  方法获取 Singleton 的唯一实例。但是以上懒汉式单例的实现是线程不安全的，
 *  在并发环境下可能出现多个 Singleton 实例的问题。要实现线程安全，有以下三种方式，
 *  都是对 getInstance() 这个方法改造，保证了懒汉式单例的线程安全：
 */

public class Singleton {
//    private Singleton() {}
//    private static Singleton single=null;
//    //静态工厂方法
//    public static Singleton getInstance() {
//         if (single == null) {
//             single = new Singleton();
//         }
//        return single;
//    }

    /**
     * 1、在 getInstance() 方法上加同步机制：
     * 在方法调用上加了同步，虽然线程安全了，但是每次都要同步，会影响性能，
     * 毕竟99%的情况下是不需要同步的。
     */
//    public static synchronized Singleton getInstance() {
//        if (single == null) {
//            single = new Singleton();
//        }
//        return single;
//    }


    /**
     * 2、双重检查锁定：
     */
    //懒汉式单例类.在第一次调用的时候实例化自己
//    public class Singleton {
//        private Singleton() {}
//        private volatile static Singleton singleton=null;
//
//        public static Singleton getInstance() {
//            if (singleton == null) {
//                synchronized (Singleton.class) {
//                    if (singleton == null) {
//                        singleton = new Singleton();
//                    }
//                }
//            }
//            return singleton;
//        }
//    }

    /**
     * 1）为什么 getInstance() 方法内需要使用两个 if (singleton == null) 进行判断呢？
     *
     * 假设高并发下，线程A、B 都通过了第一个 if 条件。若A先抢到锁，new 了一个对象，释放锁，然后线程B再抢到锁，
     * 此时如果不做第二个 if 判断，B线程将会再 new 一个对象。使用两个 if 判断，确保了只有第一次调用单例的时候才
     * 会做同步，这样也是线程安全的，同时避免了每次都同步的性能损耗。
     *
     * （2）volatile 关键字的作用？
     *
     * volatile 的作用主要是禁止指定重排序。假设在不使用 volatile 的情况下，两个线程A、B，都是第一次调用该
     * 单例方法，线程A先执行 singleton = new Singleton()，但由于构造方法不是一个原子操作，编译后会生成多条
     * 字节码指令，由于 JAVA的 指令重排序，可能会先执行 singleton 的赋值操作，该操作实际只是在内存中开辟一片
     * 存储对象的区域后直接返回内存的引用，之后 singleton 便不为空了，但是实际的初始化操作却还没有执行。如果此
     * 时线程B进入，就会拿到一个不为空的但是没有完成初始化的singleton 对象，所以需要加入volatile关键字，禁止
     * 指令重排序优化，从而安全的实现单例。
     */


    /**
     * 3、静态内部类：
     * 利用了类加载机制来保证初始化 instance 时只有一个线程，所以也是线程安全的，同时没有性能损耗，
     * 这种比上面1、2都好一些，既实现了线程安全，又避免了同步带来的性能影响。
     */
    private static class LazyHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
        return LazyHolder.INSTANCE;
    }


}